#pragma once

#include "InetAddr.hpp"
#include "Log.hpp"
#include "Common.hpp"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <sys/wait.h>
#include <thread>

#include <functional>

using namespace logModule;

const int defaultSockfd = -1;
const int backlog = 8;

class TcpServer : public NoCopy
{
    using fun_t = std::function<void(int, const InetAddr&)>;
    // using fun_t = std::function<std::string(const std::string&, const InetAddr&)>;

public:

    TcpServer(uint16_t port, fun_t task)
        :_port(port)
        ,_listenSockfd(defaultSockfd)
        ,_isRunning(false)
        ,_task(task)
    {
        ;
    }

    void init()
    {
        _listenSockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket failure";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::DEBUG) << "socket success";

        InetAddr local(_port);
        int returnNum = bind(_listenSockfd, local.getAddrPtr(), local.getAddrLen());
        if (returnNum < 0)
        {
            LOG(LogLevel::FATAL) << "bind failure";
            exit(BIND_ERR);
        }
        LOG(LogLevel::DEBUG) << "bind success";

        // 设置 socket 状态为监听，并对尝试连接的主机进入未完成队列，
        // 当三次握手后，连接会被移动到已完成队列，等待 accept() 调用
        returnNum = listen(_listenSockfd, backlog);
        if (returnNum < 0)
        {
            LOG(LogLevel::FATAL) << "listen failure";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::DEBUG) << "listen success";
    }
    
    void service(int sockfd, const InetAddr& aim)
    {        
        _task(sockfd, aim);
    }

    static void routine(TcpServer* tcp, int sockfd, const InetAddr& connect)
    {
        // this->service(sockfd, connect);
        tcp->service(sockfd, connect);
    }

    void run()
    {
        _isRunning = true;
        while (true)
        {
            struct sockaddr_in aim;
            socklen_t len = sizeof(aim);
            
            // accept 在连接队列里取出一个连接，并创造一个新 sockfd 用于交流
            int sockfd = accept(_listenSockfd, (struct sockaddr*)&aim, &len);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept failure";
                continue;
            }

            // 4. 线程池更适合做短期服务

            // 3. 多线程
            std::thread t1(routine, this, sockfd, aim);
            t1.detach();

            // 2.多进程
            // int id = fork();
            // if (id < 0)
            // {
            //     LOG(LogLevel::WARNING) << "fork failure";
            //     exit(FORK_ERR);
            // }
            // else if (id == 0)
            // {
            //     close (_listenSockfd);

            //     if (fork() > 0)     // 子进程退出，孙子进程执行完被系统回收
            //     {
            //         exit(0);
            //     }

            //     InetAddr connect(aim);

            //     service(sockfd, connect);
            //     exit(0);
            // }
            // else
            // {
            //     close(sockfd);

            //     waitpid(id, nullptr, 0);
            // }


            // 1.单进程
            // struct sockaddr_in aim;
            // socklen_t len = sizeof(aim);
            // // accept 在连接队列里取出一个连接，并创造一个新 sockfd 用于交流
            // int sockfd = accept(_listenSockfd, (struct sockaddr*)&aim, &len);
            // if (sockfd < 0)
            // {
            //     LOG(LogLevel::WARNING) << "accept failure";
            //     continue;
            // }
            // InetAddr connect(aim);
            // service(sockfd, connect);
        }
    }
    
    ~TcpServer()
    {
        ;
    }
    
private:

    uint16_t _port;
    int _listenSockfd;
    bool _isRunning;

    fun_t _task;
};